package rt.wxf_bo.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.jodconverter.core.DocumentConverter;
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.local.LocalConverter;
import org.jodconverter.local.office.LocalOfficeManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import io.minio.MinioClient;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.PutObjectArgs;
import io.minio.errors.MinioException;
import rt.wxf_bo.config.MinioConfig;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.UUID;

@Slf4j
@RestController
@RequestMapping("/convert")
@CrossOrigin(origins = "*")
public class FileConverterControllerLibAll {

    private String windowsStoragePath = "D:/pdf/myp"; // Windows 路径
    private String linuxStoragePath = "/srv/pptPreviewFiles/"; // Linux 路径

    // 共享的 LocalOfficeManager 实例
    private LocalOfficeManager officeManager;
    
    @Autowired
    private MinioConfig minioConfig;

    /**
     * 初始化 LocalOfficeManager
     */
    @PostConstruct
    public void init() throws OfficeException {
        // 配置 LibreOffice 安装路径
        officeManager = LocalOfficeManager.builder()
                .officeHome(getOfficeHomePath()) // 根据操作系统设置路径
                .taskExecutionTimeout(300000L) // 设置超时时间为 300 秒（5 分钟）
                .install()
                .build();

        // 启动 OfficeManager
        officeManager.start();
        log.info("LibreOffice 已启动");
    }

    /**
     * 销毁 LocalOfficeManager
     */
    @PreDestroy
    public void destroy() throws OfficeException {
        if (officeManager != null) {
            officeManager.stop();
            log.info("LibreOffice 已停止");
        }
    }

    /**
     * 获取当前操作系统的存储路径
     */
    private String getStoragePath() {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("win")) {
            return windowsStoragePath; // Windows 环境
        } else {
            return linuxStoragePath; // Linux 环境
        }
    }

    /**
     * 获取 LibreOffice 安装路径
     */
    private String getOfficeHomePath() {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("win")) {
            return "C:/Program Files/LibreOffice"; // Windows 路径
        } else {
            return "/usr/lib/libreoffice"; // Linux 路径
        }
    }

    @GetMapping
    public void convertAndPreview(@RequestParam String fileUrl, HttpServletResponse response) {
        try {
            log.info("开始处理文件：{}", fileUrl);

            // 获取存储路径
            String storagePath = getStoragePath();
            Path localFilePath = Paths.get(storagePath, getFileNameFromUrl(fileUrl));

            // 检查文件是否为 PDF
            if (fileUrl.toLowerCase().endsWith(".pdf")) {
                log.info("文件已经是 PDF 格式，直接处理: {}", fileUrl);
                handlePdfFile(fileUrl, localFilePath, response);
                return;
            }

            // 检查文件是否为图片
            if (isImageFile(fileUrl)) {
                log.info("文件是图片格式，转换为 PDF 处理: {}", fileUrl);
                handleImageFile(fileUrl, localFilePath, response);
                return;
            }

            // 检查文件是否为文本
            if (isTextFile(fileUrl)) {
                log.info("文件是文本格式，转换为 PDF 处理: {}", fileUrl);
                handleTextFile(fileUrl, localFilePath, response);
                return;
            }

            // 检查文件是否为压缩包
            if (isArchiveFile(fileUrl)) {
                log.info("文件是压缩包格式，不支持预览: {}", fileUrl);
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.getWriter().write("压缩包文件不支持预览");
                return;
            }

            // 其他格式的文件，使用 jodconverter 转换为 PDF
            String outputFileName = getOutputFileName(fileUrl); // 根据文件类型生成 PDF 文件名
            Path pdfFilePath = Paths.get(storagePath, outputFileName);

            // 检查 PDF 文件是否已存在
            if (Files.exists(pdfFilePath)) {
                log.info("PDF 文件已存在，直接返回: {}", pdfFilePath);
                try (PDDocument pdfDocument = PDDocument.load(pdfFilePath.toFile());
                     OutputStream outputStream = response.getOutputStream()) {
                    renderPdf(pdfDocument, outputStream);
                }
                return;
            }

            byte[] fileContent;
            if (Files.exists(localFilePath)) {
                // 如果文件已存在，直接从本地读取
                fileContent = Files.readAllBytes(localFilePath);
                log.info("从本地目录读取文件内容: {}", localFilePath);
            } else {
                // 如果文件不存在，则从 URL 下载文件并保存到本地目录
                fileContent = downloadFileFromUrl(fileUrl);
                Files.createDirectories(localFilePath.getParent()); // 确保目录存在
                Files.write(localFilePath, fileContent);
                log.info("文件下载完成，大小：{} 字节，已保存到本地目录: {}", fileContent.length, localFilePath);
            }

            // 使用 jodconverter 将文件转换为 PDF
            pdfFilePath = convertToPdfUsingJodConverter(localFilePath);

            // 读取生成的 PDF 文件并输出整个 PDF
            try (PDDocument pdfDocument = PDDocument.load(pdfFilePath.toFile());
                 OutputStream outputStream = response.getOutputStream()) {
                renderPdf(pdfDocument, outputStream);
            }
        } catch (Exception e) {
            log.error("处理失败", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 处理 PDF 文件
     */
    private void handlePdfFile(String fileUrl, Path localFilePath, HttpServletResponse response) throws Exception {
        byte[] fileContent;
        if (Files.exists(localFilePath)) {
            // 如果文件已存在，直接从本地读取
            fileContent = Files.readAllBytes(localFilePath);
            log.info("从本地目录读取文件内容: {}", localFilePath);
        } else {
            // 如果文件不存在，则从 URL 下载文件并保存到本地目录
            fileContent = downloadFileFromUrl(fileUrl);
            Files.createDirectories(localFilePath.getParent()); // 确保目录存在
            Files.write(localFilePath, fileContent);
            log.info("文件下载完成，大小：{} 字节，已保存到本地目录: {}", fileContent.length, localFilePath);
        }

        // 读取 PDF 文件并输出整个 PDF
        try (PDDocument pdfDocument = PDDocument.load(new ByteArrayInputStream(fileContent));
             OutputStream outputStream = response.getOutputStream()) {
            renderPdf(pdfDocument, outputStream);
        }
    }

    /**
     * 处理图片文件
     */
    private void handleImageFile(String fileUrl, Path localFilePath, HttpServletResponse response) throws Exception {
        byte[] fileContent;
        if (Files.exists(localFilePath)) {
            // 如果文件已存在，直接从本地读取
            fileContent = Files.readAllBytes(localFilePath);
            log.info("从本地目录读取文件内容: {}", localFilePath);
        } else {
            // 如果文件不存在，则从 URL 下载文件并保存到本地目录
            fileContent = downloadFileFromUrl(fileUrl);
            Files.createDirectories(localFilePath.getParent()); // 确保目录存在
            Files.write(localFilePath, fileContent);
            log.info("文件下载完成，大小：{} 字节，已保存到本地目录: {}", fileContent.length, localFilePath);
        }

        // 将图片转换为 PDF
        try (PDDocument pdfDocument = new PDDocument();
             OutputStream outputStream = response.getOutputStream()) {
            BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileContent));
            PDPage page = new PDPage(new PDRectangle(image.getWidth(), image.getHeight()));
            pdfDocument.addPage(page);

            PDImageXObject pdImage = PDImageXObject.createFromByteArray(pdfDocument, fileContent, "image");
            try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                contentStream.drawImage(pdImage, 0, 0);
            }

            pdfDocument.save(outputStream);
        }
    }

    /**
     * 处理文本文件
     */
    private void handleTextFile(String fileUrl, Path localFilePath, HttpServletResponse response) throws Exception {
        byte[] fileContent;
        if (Files.exists(localFilePath)) {
            // 如果文件已存在，直接从本地读取
            fileContent = Files.readAllBytes(localFilePath);
            log.info("从本地目录读取文件内容: {}", localFilePath);
        } else {
            // 如果文件不存在，则从 URL 下载文件并保存到本地目录
            fileContent = downloadFileFromUrl(fileUrl);
            Files.createDirectories(localFilePath.getParent()); // 确保目录存在
            Files.write(localFilePath, fileContent);
            log.info("文件下载完成，大小：{} 字节，已保存到本地目录: {}", fileContent.length, localFilePath);
        }

        // 将文本转换为 PDF
        try (PDDocument pdfDocument = new PDDocument();
             OutputStream outputStream = response.getOutputStream()) {
            PDPage page = new PDPage(PDRectangle.A4);
            pdfDocument.addPage(page);

            try (PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page)) {
                contentStream.setFont(PDType1Font.HELVETICA_BOLD, 12);
                contentStream.beginText();
                contentStream.newLineAtOffset(50, 750);

                List<String> lines = Files.readAllLines(localFilePath);
                for (String line : lines) {
                    contentStream.showText(line);
                    contentStream.newLineAtOffset(0, -15);
                }

                contentStream.endText();
            }

            pdfDocument.save(outputStream);
        }
    }

    /**
     * 检查文件是否为图片
     */
    private boolean isImageFile(String fileUrl) {
        String lowerCaseUrl = fileUrl.toLowerCase();
        return lowerCaseUrl.endsWith(".jpg") || lowerCaseUrl.endsWith(".jpeg") ||
                lowerCaseUrl.endsWith(".png") || lowerCaseUrl.endsWith(".gif") ||
                lowerCaseUrl.endsWith(".bmp");
    }

    /**
     * 检查文件是否为文本
     */
    private boolean isTextFile(String fileUrl) {
        String lowerCaseUrl = fileUrl.toLowerCase();
        return lowerCaseUrl.endsWith(".txt") || lowerCaseUrl.endsWith(".csv");
    }

    /**
     * 检查文件是否为压缩包
     */
    private boolean isArchiveFile(String fileUrl) {
        String lowerCaseUrl = fileUrl.toLowerCase();
        return lowerCaseUrl.endsWith(".zip") || lowerCaseUrl.endsWith(".rar") ||
                lowerCaseUrl.endsWith(".7z") || lowerCaseUrl.endsWith(".tar");
    }

    /**
     * 渲染整个 PDF 文件
     */
    private void renderPdf(PDDocument pdfDocument, OutputStream outputStream) throws IOException {
        log.info("PDF 文件已加载，总页数：{}", pdfDocument.getNumberOfPages());

        // 将整个 PDF 文件写入输出流
        pdfDocument.save(outputStream);
        log.info("PDF 文件已发送到浏览器");
    }

    /**
     * 从文件 URL 生成唯一的文件名
     */
    private String getFileNameFromUrl(String fileUrl) {
        return String.valueOf(fileUrl.hashCode()) + getFileExtension(fileUrl); // 使用哈希值作为文件名
    }

    /**
     * 根据文件 URL 获取文件扩展名
     */
    private String getFileExtension(String fileUrl) {
        String lowerCaseUrl = fileUrl.toLowerCase();
        if (lowerCaseUrl.endsWith(".doc")) {
            return ".doc";
        } else if (lowerCaseUrl.endsWith(".docx")) {
            return ".docx";
        } else if (lowerCaseUrl.endsWith(".xls")) {
            return ".xls";
        } else if (lowerCaseUrl.endsWith(".xlsx")) {
            return ".xlsx";
        } else if (lowerCaseUrl.endsWith(".ppt")) {
            return ".ppt";
        } else if (lowerCaseUrl.endsWith(".pptx")) {
            return ".pptx";
        } else if (lowerCaseUrl.endsWith(".pdf")) {
            return ".pdf";
        } else if (lowerCaseUrl.endsWith(".jpg") || lowerCaseUrl.endsWith(".jpeg")) {
            return ".jpg";
        } else if (lowerCaseUrl.endsWith(".png")) {
            return ".png";
        } else if (lowerCaseUrl.endsWith(".gif")) {
            return ".gif";
        } else if (lowerCaseUrl.endsWith(".bmp")) {
            return ".bmp";
        } else if (lowerCaseUrl.endsWith(".txt")) {
            return ".txt";
        } else if (lowerCaseUrl.endsWith(".csv")) {
            return ".csv";
        } else if (lowerCaseUrl.endsWith(".zip")) {
            return ".zip";
        } else if (lowerCaseUrl.endsWith(".rar")) {
            return ".rar";
        } else if (lowerCaseUrl.endsWith(".7z")) {
            return ".7z";
        } else if (lowerCaseUrl.endsWith(".tar")) {
            return ".tar";
        } else {
            throw new IllegalArgumentException("不支持的文件格式: " + fileUrl);
        }
    }

    /**
     * 根据文件类型生成 PDF 文件名
     */
    private String getOutputFileName(String fileUrl) {
        String fileName = getFileNameFromUrl(fileUrl);
        if (fileName.endsWith(".pdf")) {
            return fileName; // 如果是 PDF 文件，直接返回原文件名
        } else if (fileName.endsWith(".doc")) {
            return fileName.replace(".doc", ".pdf");
        } else if (fileName.endsWith(".docx")) {
            return fileName.replace(".docx", ".pdf");
        } else if (fileName.endsWith(".xls")) {
            return fileName.replace(".xls", ".pdf");
        } else if (fileName.endsWith(".xlsx")) {
            return fileName.replace(".xlsx", ".pdf");
        } else if (fileName.endsWith(".ppt")) {
            return fileName.replace(".ppt", ".pdf");
        } else if (fileName.endsWith(".pptx")) {
            return fileName.replace(".pptx", ".pdf");
        } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
                fileName.endsWith(".png") || fileName.endsWith(".gif") ||
                fileName.endsWith(".bmp")) {
            return fileName.replace(".jpg", ".pdf")
                    .replace(".jpeg", ".pdf")
                    .replace(".png", ".pdf")
                    .replace(".gif", ".pdf")
                    .replace(".bmp", ".pdf");
        } else if (fileName.endsWith(".txt") || fileName.endsWith(".csv")) {
            return fileName.replace(".txt", ".pdf")
                    .replace(".csv", ".pdf");
        } else {
            throw new IllegalArgumentException("不支持的文件格式: " + fileName);
        }
    }

    /**
     * 从 URL 下载文件（支持 HTTP/HTTPS）
     */
    private byte[] downloadFileFromUrl(String fileUrl) throws Exception {
        if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
            // 处理标准的 HTTP/HTTPS URL（包括 MinIO 的 HTTP 链接）
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            try (InputStream inputStream = connection.getInputStream();
                 ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
                byte[] data = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(data)) != -1) {
                    buffer.write(data, 0, bytesRead);
                }
                return buffer.toByteArray();
            } finally {
                connection.disconnect();
            }
        } else {
            throw new IllegalArgumentException("不支持的 URL 格式: " + fileUrl);
        }
    }

    /**
     * 使用 jodconverter 将文件转换为 PDF
     */
    private Path convertToPdfUsingJodConverter(Path inputFilePath) throws OfficeException, IOException {
        int retryCount = 3; // 最大重试次数
        OfficeException lastException = null;

        for (int i = 0; i < retryCount; i++) {
            try {
                String outputDir = inputFilePath.getParent().toString();
                String inputFileName = inputFilePath.getFileName().toString();
                String outputFileName = getOutputFileName(inputFileName);
                Path outputFilePath = Paths.get(outputDir, outputFileName);

                if (Files.exists(outputFilePath)) {
                    log.info("PDF 文件已存在，无需转换: {}", outputFilePath);
                    return outputFilePath;
                }

                DocumentConverter converter = LocalConverter.make(officeManager);
                converter.convert(inputFilePath.toFile()).to(outputFilePath.toFile()).execute();

                log.info("文件已转换为 PDF: {}", outputFilePath);
                return outputFilePath;
            } catch (OfficeException e) {
                lastException = e;
                log.warn("文件转换失败，重试次数: {}", i + 1, e);
            }
        }

        throw lastException; // 重试次数用尽后抛出异常
    }
    
    /**
     * 接收docx文件流，转换为pdf并返回流
     * 
     * @param file 上传的docx文件
     * @param response HTTP响应
     */
    @PostMapping("/docx-to-pdf")
    public String convertDocxToPdf(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws Exception {
        try {
            log.info("开始处理docx文件转换: {}", file.getOriginalFilename());

            // 获取存储路径
            String storagePath = getStoragePath();
            
            // 生成临时文件名
            String tempFileName = UUID.randomUUID().toString() + ".docx";
            Path tempFilePath = Paths.get(storagePath, tempFileName);
            String pdfFileName = UUID.randomUUID().toString() + ".pdf";
            Path pdfFilePath = Paths.get(storagePath, pdfFileName);
            
            try {
                // 确保目录存在
                Files.createDirectories(tempFilePath.getParent());
                
                // 保存上传的文件到临时位置
                try (InputStream inputStream = file.getInputStream()) {
                    Files.copy(inputStream, tempFilePath);
                }
                log.info("docx文件已保存到临时位置: {}", tempFilePath);
                
                // 转换docx到pdf
                pdfFilePath = convertToPdfUsingJodConverter(tempFilePath);
                
                // 直接使用MinIO客户端上传文件
                String fileUrl = uploadFileToMinio(pdfFilePath, pdfFileName, "application/pdf");
                log.info("PDF文件已上传到MinIO，文件路径: {}", fileUrl);
                
                return fileUrl;
            } finally {
                // 清理临时文件
                try {
                    if (Files.exists(tempFilePath)) {
                        Files.delete(tempFilePath);
                        log.info("已删除临时docx文件: {}", tempFilePath);
                    }
                    if (Files.exists(pdfFilePath)) {
//                        Files.delete(pdfFilePath);
                        log.info("已删除临时pdf文件: {}", pdfFilePath);
                    }
                } catch (IOException e) {
                    log.warn("删除临时文件失败", e);
                }
            }
        } catch (Exception e) {
            log.error("docx到pdf转换失败", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            return null;
        }
    }


    /**
     * 将本地文件上传到MinIO存储
     * @param filePath 本地文件路径
     * @param fileName 文件名
     * @param contentType 文件内容类型
     * @return 上传后的文件访问路径
     */
    private String uploadFileToMinio(Path filePath, String fileName, String contentType) throws IOException, MinioException, NoSuchAlgorithmException, InvalidKeyException {
        // 创建 MinIO 客户端
        MinioClient minioClient = MinioClient.builder()
                .endpoint(minioConfig.getEndpoint())
                .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                .build();

        // 检查存储桶是否存在，如果不存在则创建
        String bucketName = minioConfig.getBucketName();
        boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!isExist) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }

        // 上传文件到 MinIO
        try (InputStream inputStream = Files.newInputStream(filePath)) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .stream(inputStream, Files.size(filePath), -1)
                            .contentType(contentType)
                            .build());
        }

        // 生成文件链接
        return minioConfig.getEndpoint() + "/" + bucketName + "/" + fileName;
    }
}